﻿//     Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Gateway.Console.Models;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Management.Scheduler;
using Microsoft.WindowsAzure.Management.Scheduler.Models;
using Microsoft.WindowsAzure.Scheduler;
using Microsoft.WindowsAzure.Scheduler.Models;

namespace Gateway.Console.Services.Azure
{
    public class SchedulerManagement : AzureServiceManagement
    {
        public SchedulerManagement(AuthenticationResult accessToken, string subscriptionId)
            : base(accessToken, subscriptionId)
        {

        }

        public SchedulerManagement(AuthenticationResult accessToken, string subscriptionId, ConsoleContext db) 
            : base(accessToken, subscriptionId, db)
        {
        }

        private async Task WriteStatusAsync(AnalyticsModel model, DateTime date, string message, bool success)
        {
            System.Diagnostics.Trace.TraceInformation("{0} {1} {2}", date, success, message);
            await this._database.AddStatusAsync<AnalyticsStatus>(model.Id, date, success, message);
        }

        private async Task WriteStatusAsync(AnalyticsModel model, bool success, string message, params object[] args)
        {
            await WriteStatusAsync(model, DateTime.UtcNow, String.Format(message, args), success);
        }

        private void WriteStatus(AnalyticsModel model, bool success, string message, params object[] args)
        {
            WriteStatusAsync(model, success, message, args).Wait();
        }

        static IEnumerable<Tuple<string, string, JobRecurrenceFrequency>> ScheduledJobDefinitions = new[]
        {
            Tuple.Create("calculate-weekly-aggregates", "aggregate-rollup-weekly", JobRecurrenceFrequency.Week),
            Tuple.Create("check-weblog-partitions", "check-weblog-partitions", JobRecurrenceFrequency.Hour),
        };

        public async Task<bool> CreateOrUpdateAggregateJobsAsync(AnalyticsModel model, bool create)
        {
            try
            {
                string jobCollectionName = model.ClusterName;
                var creds = GetAzureCredentials();
                await TryCreateSchedulingService(model, creds);
                using (var schedulerServiceClient = CloudContext.Clients.CreateSchedulerManagementClient(creds))
                {
                    await TryRegisteringSchedulingResourceProvider(model, schedulerServiceClient);
                    if (!await DoesJobCollectionExistAsync(schedulerServiceClient, model.ClusterName, jobCollectionName))
                    {
                        await WriteStatusAsync(model, true, "Creating scheduling container [{0}]", jobCollectionName);
                        var result3 = await schedulerServiceClient.JobCollections.CreateAsync(model.ClusterName, jobCollectionName, new JobCollectionCreateParameters
                        {
                            IntrinsicSettings = new JobCollectionIntrinsicSettings
                            {
                                Plan = JobCollectionPlan.Free,
                                Quota = new JobCollectionQuota 
                                { 
                                    MaxJobCount = 5,
                                    MaxJobOccurrence = 1,
                                    MaxRecurrence = new JobCollectionMaxRecurrence 
                                    { 
                                        Frequency = JobCollectionRecurrenceFrequency.Hour,
                                        Interval = 1,
                                    }
                                },
                            }
                        });
                    }
                }
                using (var schedulerClient = CloudContext.Clients.CreateSchedulerClient(creds, model.ClusterName, jobCollectionName))
                {
                    foreach (var jobDefinition in ScheduledJobDefinitions)
                    {
                        await CreateOrUpdateJobAsync(model, schedulerClient, jobDefinition.Item1, jobDefinition.Item2, jobDefinition.Item3, 1);
                    }
                }

                return true;
            }
            catch (CloudException ex)
            {
                if (ex.ErrorCode == "ForbiddenError")
                {
                    WriteStatus(model, false, "Creation of scheduled jobs is forbidden for this subscription, Please ensure that the Scheduler is selected in Azure Preview features for this subscription. Details: {0}", ex);
                }
                else
                {
                    WriteStatus(model, false, "Failed to create scheduled analytics aggregation jobs. Details: {0}", ex);
                }
            }
            catch (Exception ex)
            {
                WriteStatus(model, false, "Failed to create scheduled analytics aggregation jobs. Details: {0}", ex);
            }
            return false;
        }

        public async Task DeleteAggregateJobsAsync(AnalyticsModel model)
        {
            try
            {
                // We leave the job collection as there's no way to (via the managed API) to check if there's no more jobs in the collection
                using (var schedulerClient = CloudContext.Clients.CreateSchedulerClient(GetAzureCredentials(), model.ClusterName, model.ClusterName))
                {
                    foreach (var jobDefinition in ScheduledJobDefinitions)
                    {
                        await DeleteJobAsync(model, schedulerClient, jobDefinition.Item1);
                    }
                }
            }
            catch (Exception ex)
            {
                WriteStatus(model, false, "Failed to delete scheduled analytics aggregation jobs. Details: {0}", ex);
            }
        }

        private async Task TryCreateSchedulingService(AnalyticsModel model, SubscriptionCloudCredentials creds)
        {
            try
            {
                using (var cronClient = CloudContext.Clients.CreateCloudServiceManagementClient(creds))
                {
                    var result = await cronClient.CloudServices.CreateAsync(model.ClusterName, new CloudServiceCreateParameters
                    {
                        Description = "Service Gateway Analytics Aggregation",
                        // TODO: Add region selection when Scheduler service expands to more than 1 location
                        GeoRegion = "South Central US",
                        Label = "CS-SouthCentralUS-scheduler",
                    });
                }
            }
            catch
            {

            }
        }

        private async Task TryRegisteringSchedulingResourceProvider(AnalyticsModel model, SchedulerManagementClient schedulerServiceClient)
        {
            try
            {
                var result2 = await schedulerServiceClient.RegisterResourceProviderAsync();
                await WriteStatusAsync(model, true, "Registering scheduling resource provider in subscription");
            }
            catch
            {

            }
        }

        private async Task<bool> DoesJobCollectionExistAsync(SchedulerManagementClient schedulerServiceClient, string cronServiceName, string jobCollectionName)
        {
            try
            {
                var jobCollection = await schedulerServiceClient.JobCollections.GetAsync(cronServiceName, jobCollectionName);
                return jobCollection.State == JobCollectionState.Started;
            }
            catch
            {
                return false;
            }
        }

        private async Task<bool> CreateOrUpdateJobAsync(AnalyticsModel model, 
            SchedulerClient schedulerClient, 
            string jobId, 
            string jobIndex, 
            JobRecurrenceFrequency frequency, 
            int interval)
        {
            await WriteStatusAsync(model, true, "Creating scheduled job [{0}] to invoke job index [{1}] every {2} {3}",
                jobId, jobIndex, interval, frequency);
            var result = await schedulerClient.Jobs.CreateOrUpdateAsync(jobId, new JobCreateOrUpdateParameters
            {
                Action = new JobAction
                {
                    Type = JobActionType.Http,
                    Request = new JobHttpRequest
                    {
                        Uri = new Uri(model.AnalyticsServerHost + "/api/AnalyticsApi/" + jobIndex),
                        Method = "POST",
                        Headers = new Dictionary<string, string>() {
                            {"content-Type", "test/plain"},
                        },
                        Body = "",
                    }
                },
                StartTime = DateTime.UtcNow,
                Recurrence = new JobRecurrence
                {
                    Frequency = frequency,
                    Interval = interval,
                    /*Schedule = new JobRecurrenceSchedule 
                    {
                        //Days = new JobScheduleDay[] { JobScheduleDay.Sunday },
                    },*/
                }
            });
            return true;
        }

        private async Task<bool> DeleteJobAsync(AnalyticsModel model, SchedulerClient schedulerClient, string jobId)
        {
            await WriteStatusAsync(model, true, "Deleting scheduled job [{0}]", jobId);
            await schedulerClient.Jobs.DeleteAsync(jobId);
            return true;
        }
    }
}